package EngineSelector;

import Common.Intersection;
import Database.CommonOperations;
import Database.DbConstants;
import Database.Document;
import Database.Keyword;
import Database.KeywordHashTable;
import Database.NamedMatrix;
import Query.Dispatcher;
import Query.Priority;
import Query.SearchEnginesTypes;
import com.db4o.Db4o;
import com.db4o.ObjectContainer;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;

public class EngineSelector {
    
    Vector<SearchEnginesTypes> engines = new Vector<SearchEnginesTypes>();
    
    /** Creates a new instance of EngineSelector */
    public EngineSelector() {
        engines.add(SearchEnginesTypes.GOOGLE);
        engines.add(SearchEnginesTypes.YAHOO);
        engines.add(SearchEnginesTypes.LIVE_SEARCH);
    }
    
   public Vector<Priority> SelectEnginePriorities(String query)
    {
        ///process queries...
        String[] queryKeywords = processQuery(query);
       
        String[] topMatches = getTopMatches(queryKeywords);
        
        ///dispatcher;
        Dispatcher d = new Dispatcher();
        Vector<Vector<Vector<Document>>> ShitHappens = new Vector<Vector<Vector<Document>>>();
        for(String s : topMatches)
            ShitHappens.add(d.PerformQueries(s, 10));
        
        ///group result vectors by search engine...
        Vector<Priority> priorities = new Vector<Priority>();
        int i = 0;
        Vector<Vector<Document>> ResultsBySearchEngine = null;
        for(SearchEnginesTypes t : engines)
        {
            ResultsBySearchEngine = new Vector<Vector<Document>>();
            for(Vector<Vector<Document>> queryVector : ShitHappens)
            {
                for(Vector<Document> searchEngineResultSet : queryVector)
                {
                    try{
                        if(searchEngineResultSet.firstElement().getSearchEngine() == t)
                            ResultsBySearchEngine.add(searchEngineResultSet);
                    }
                    catch(Exception e)
                    {}
                }
            }
            priorities.add(new Priority(t, Intersection.IntersectDocumentsSize(ResultsBySearchEngine)));
        }
        
        return priorities;
                    
    }
    
    private String[] getTopMatches(String[] queryKeywords)
    {
        
        ///open and close connection so we recreate the database if it does not exist...
        ObjectContainer db = Db4o.openFile(DbConstants.DB_FILE_NAME); 
        db.close();
        db = Db4o.openFile(DbConstants.DB_FILE_NAME); 
        
        ///retrieve relevant data from the database...
        NamedMatrix freqMM = null;
        KeywordHashTable keywords = null;
        try
        {            
            ///get the keyword's hash table...
            keywords = CommonOperations.getKeywords(db);
            
            ///if there was no hashtable...
            if(keywords == null)
                keywords = new KeywordHashTable();

            ///get the fractMM matrix...
            freqMM = CommonOperations.getFreqMM(db);
            
            ///if there was no freqMM...
            if(freqMM == null)
                freqMM = new NamedMatrix(DbConstants.FREQMM);                    
            
            ///create a vectorial representation of the
            ///query similar to the one in the freqMM...
            NamedMatrix queryMatrix = new NamedMatrix(
                freqMM.getRowDimension(),
                1,
                "queryMatrix"
                );
            for(int i = 0; i < queryKeywords.length; i++)        
                if(keywords.get(new Keyword(queryKeywords[i])).intValue() < queryMatrix.getRowDimension())
                    queryMatrix.set(keywords.get(new Keyword(queryKeywords[i])).intValue(), 0, 1);
            
            ///compare that vector against all other vectors in freqMM...
            int[] mostSimilarVectors = getTopMatchesVectors(freqMM, queryMatrix);
            
            ///get the queries
            return getQueries(freqMM, keywords, mostSimilarVectors); 
           
        }
        catch(Exception e)
        {   
            ///return null...
            return null;
        }
        finally
        {
            db.close();
        }
        
    }
    
    private String[] getQueries(NamedMatrix m, KeywordHashTable keywords, int[] queries) 
    {
        ///convert pairs <keyword, integer> to an ordered vector...
        Set keywordSet = keywords.entrySet();
        Keyword[] k = new Keyword[m.getRowDimension()];
        Iterator<java.util.Map.Entry> it = keywordSet.iterator();
        java.util.Map.Entry entry = null;
        while(it.hasNext())
        {
            entry = it.next();
            k[keywords.get((Keyword) entry.getKey()).intValue()] = (Keyword) entry.getKey();
        }
        
        String [] results = new String[queries.length];
        
        for(int i = 0; i < queries.length; i++)
        {
            results[i] = "";
            for(int j = 0; j < m.getRowDimension(); j++)
            {
                if(m.get(j,queries[i]) == 1)
                    results[i] += " " + k[j].getKeyword();
            }
        }
        
        return results;
    }
    
    private int[] getTopMatchesVectors(NamedMatrix m, NamedMatrix v)
    {
        final int howMany = 3;
        int[] mostSimilar = new int[howMany];
        double[] mostSimilarValues = new double[howMany];
        
        double numerator = 0;
        double sigmaofsquares1 = 0;
        double sigmaofsquares2 = 0;
        double denominator = 0;
        double cosine = 0;

        for(int i = 0; i < m.getColumnDimension(); i++)
        {
            ///perform the calculation of the cosine..
            numerator = 0;
            sigmaofsquares1 = 0;
            sigmaofsquares2 = 0;
            denominator = 0;
            cosine = 0;
            
            for(int j = 0; j < m.getRowDimension(); j++)
            {                
                numerator += m.get(j,i) * v.get(j,0);
                sigmaofsquares1 += m.get(j,i) * m.get(j,i);
                sigmaofsquares2 += v.get(j,0) * v.get(j,0);
            }
            
            cosine = numerator/(Math.sqrt(sigmaofsquares1) * Math.sqrt(sigmaofsquares2));
            
            ///perform a simple insertion sort to keep a sorted list of the fucking top matches
            for(int k = 0; k < mostSimilarValues.length; k++)
            {
                ///if this value is bigger, perform insertion...
                if(mostSimilarValues[k] < cosine)
                {
                    for(int l = mostSimilarValues.length - 2; l >= k; l--)
                    {
                        mostSimilarValues[l+1] = mostSimilarValues[l];
                        mostSimilar[l+1] = mostSimilar[l];
                    }
                    mostSimilarValues[k] = cosine;
                    mostSimilar[k] = i;
                    break;
                }
            }
        }
        
        return mostSimilar;
    }
    
    private String[] processQuery(String query)
    {
        ///split query string...
        String[] queryKeywords = query.split(" ");
        
        ///apply filters to the query string...
        Common.Filters.Filter f = new Common.Filters.Filter();
        int count = 0;
        for(int i = 0; i < queryKeywords.length; i++)
            if((queryKeywords[i] = f.Filtering(queryKeywords[i])) != null) count++;
        
        String[] result = new String[count];
        
        for(int i = 0, j = 0; i < queryKeywords.length; i++)
            if(queryKeywords[i] != null) result[j++] = queryKeywords[i];
        
        return result;
    }
}
